package com.diaohw.platform.framework.db.filter;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.druid.filter.FilterChain;
import com.alibaba.druid.filter.FilterEventAdapter;
import com.alibaba.druid.proxy.jdbc.JdbcParameter;
import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy;
import com.alibaba.druid.proxy.jdbc.StatementProxy;
import com.diaohw.platform.common.LogProcessAPI;
import com.diaohw.platform.common.enums.CodeMsgEnum;
import com.diaohw.platform.common.obj.log.DbLog;
import com.diaohw.platform.common.obj.thread.LoginThread;
import com.diaohw.platform.common.obj.thread.RequestThread;
import com.diaohw.platform.common.properties.LogProperties;
import com.diaohw.platform.common.util.JsonUtil;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * @author 刁海文
 * @Remark <a href="https://blog.csdn.net/m0_38005658/article/details/90699186">参考地址</a>
 * @since 2023/6/28 22:07
 */
@Component
public class DruidAdapter extends FilterEventAdapter {

    private final Logger logger = LoggerFactory.getLogger("Db-Filter");
    Optional<LogProcessAPI> logProcessAPI;
    @Resource
    private
    LogProperties logProperties;

    @PostConstruct
    public void init() {
        try {
            LogProcessAPI bean = SpringUtil.getBean(LogProcessAPI.class);
            logProcessAPI = Optional.ofNullable(bean);
        } catch (Exception e) {
            logProcessAPI = Optional.empty();
        }

    }

    @Override
    protected void statementExecuteAfter(StatementProxy statement, String sql, boolean result) {
        super.statementExecuteAfter(statement, sql, result);
    }

    @Override
    public boolean preparedStatement_execute(FilterChain chain, PreparedStatementProxy statement) throws SQLException {
        if (!logProperties.isDb()) {
            return chain.preparedStatement_execute(statement);
        }

        DbLog log = new DbLog();
        try {
            log.setSqlContent(statement.getRawObject().toString())
                    .setSqlTemplate(statement.getSql())
                    .setRequestTime(LocalDateTime.now())
                    .setRequestId(RequestThread.getThreadRequestId())
                    .setLinkId(RequestThread.getThreadLinkId())
                    .setApplicationName(SpringUtil.getApplicationName())
                    .setApplicationIP(RequestThread.LOCAL_IP)
            ;

            if (LoginThread.isLogin()) {
                log.setLoginId(RequestThread.getThreadLocal().getLoginId())
                        .setLoginUsername(RequestThread.getThreadLocal().getLoginUsername());
            }
            //抽象方法
            statementExecuteBefore(statement, statement.getSql());
            // 执行SQL
            boolean execute = chain.preparedStatement_execute(statement);
            // 获取入参
            Map<String, Object> map = new HashMap<>();
            for (Map.Entry<Integer, JdbcParameter> entry : statement.getParameters().entrySet()) {
                map.put(entry.getKey().toString(), entry.getValue().getValue());
            }
            log.setSqlParameter(JsonUtil.toJsonString(map));
            // 非select 语句时可获取更改的行数
            log.setSqlRowCount(statement.getUpdateCount());
            // 获取SQL执行类型
            if (log.getSqlRowCount() == -1) {
                log.setSqlType("SELECT");
            } else if (log.getSqlContent().startsWith("INSERT")) {
                log.setSqlType("INSERT");
            } else if (log.getSqlContent().startsWith("UPDATE")) {
                log.setSqlType("UPDATE");
            } else if (log.getSqlContent().startsWith("DELETE")) {
                log.setSqlType("DELETE");
            } else {
                log.setSqlType("OTHER");
            }

            //抽象方法，后面我们会重写这个方法
            this.statementExecuteAfter(statement, statement.getSql(), execute);

            log.setStatus(CodeMsgEnum.SUCCESS.status);
            log.setMsg(CodeMsgEnum.SUCCESS.describe);
            return execute;

        } catch (SQLException | RuntimeException | Error error) {
            // SQL执行异常
            log.setStatus(CodeMsgEnum.EXCEPTION.status);
            log.setMsg(CodeMsgEnum.EXCEPTION.filterAttr(error.getMessage()));

            //抽象方法
            statement_executeErrorAfter(statement, statement.getSql(), error);
            throw error;
        } finally {
            log.setResponseTime(LocalDateTime.now())
                    .computeElapsed();
            logProcessAPI.ifPresent(api -> api.db(log));
        }


    }

}
